const path = require("path");
const fs = require("fs");
const dotenv = require('dotenv')
const express = require("express");
const favicon = require("serve-favicon");
const ejs = require("ejs");
const Mock = require("mockjs");
const cookieParser = require("cookie-parser");
const session = require("express-session");
const mysql = require("mysql2");
const { createServer } = require("http");
const { Server } = require("socket.io");
const process = require('process');
const { connectLogger } = require('./utils/logger');

dotenv.config()

const app = express();
const httpServer = createServer(app);
// 把 socket 绑定到服务上
const io = new Server(httpServer);

let friends = [];
io.on("connection", (scoket) => {
  //客户端发送 message 给服务端的时候的事件, 用户发送过消息这个会触发;
  socket.on("message", function (msg) {});
  socket.on("disconnect", function (msg) {});
  //给当前连接的客户端回传一条消息;
  socket.emit("message", {});
  //给非当前的所有用户发送推送消息;
  socket.broadcast.emit("message", {});
  //你可以给客户端发送各种事件,名字你找自己起;
  socket.emit("open", "sb");
  socket.emit("hehe", "sb");
  socket.emit("cnblogs", "sb");

  console.log("object", "[-scoket");
  // 接收客户端发送的消息,并广播出去
  scoket.on("message", (message) => {
    console.log(message, "[-scoket");
    // 向所有的客户端广播消息
    if (message.msg == undefined) {
      if (message.out) {
        // io.emit("message",mes);
        friends.splice(friends.indexOf(message.out), 1);
        io.emit("message", { friends: friends, out: message.out });
      } else {
        friends.push(message);
        console.log("friends" + friends);
        io.emit("message", { friends: friends, add: message });
      }
    } else {
      // console.log(mes);
      io.emit("message", message);
    }
  });
});

// 注册路径别名
require("module-alias/register");

/**
 * 请求解析
 * 场景错误一
 * 报错：PayloadTooLargeError: request entity too large
 * 原因：Express 解析请求时，系统默认大小为100kb
 */
app.use(express.json());
app.use(
  express.urlencoded({
    extended: false,
  })
);

/* app.set("view engine", "html");                      // 配置输出内容
app.set("views", path.resolve(__dirname, "views"));  // 指定模板文件存放位置
app.engine("html", ejs.__express);                   // 配置模板引擎 */

// 指定模板文件存放位置
app.set("views", path.resolve(__dirname, "views"));
// 设置默认的模板引擎为 ejs
app.set("view engine", "ejs");
// 为 html 扩展名注册 ejs 模板引擎
app.engine("html", ejs.renderFile);

// 日志
app.use(connectLogger)

/**
 * 中间件
 * 中间件级别
 * 1. app 级别，注册时一定是在最顶层，通过使用 app.use() 加载
 * 2. router 级别
 * 使用场景
 * 1. 异常处理，既可以是 app 级别，也可以是 router 级别
 */
function middleware(err, req, res, next) {
  // 异常处理
  // 处理业务功能，然后转交控制权---next()
  // 响应请求--结束请求-->当作路由的处理函数
}

// 使用中间件
// app.get('/middleware', [/** middleware */], () => {

// });

/**
 * __dirname 、__filename 是每个模块文件中默认存在的变量，可以直接使用不需要引入。
 * @var __dirname 当前模块的目录名
 * @var __filename 当前模块的文件名
 * http://127.0.0.1:8081/api/v1/login?phone=15297677712&password=123456
 */
/**
 * path.resolve()与path.join()的区别
 * 1. 1. 对于以 / 开始的路径片段，path.join 只是简单的将该路径片段进行拼接，而 path.resolve 将以 / 开始的路径片段作为根目录，在此之前的路径将会被丢弃
 * 2. 2. path.resolve 总是返回一个相对于当前的工作目录（working directory）的绝对路径。
 */

// express.static(root, [options]) 是 Express 中的内置中间件函数，用于处理静态文件
app.use("/public", express.static(path.join(__dirname, "public")));

// 定义 icon 图标
// app.use([path,] callback) 中的 callback 既可以是 router 对象又可以是函数
// path 省略，则对所有的请求都有作用
app.use(favicon(path.join(__dirname, "favicon.ico")));

/**
 * 注册路由
 * 增加不同的前缀，以防不同的路由模块中定义相同的 URI
 */
app.use("/api", require("./routes/api/index"));
// app.use("/api/v1/user", require("./routes/api"));

// 管理后台
// app.use("/management", require("./routes/management/index"));


// 加入本地对象
app.locals.uname = "Brad";
app.locals.vehicle = "Jeep";
app.locals.terrain = "Mountains";
app.locals.climate = "Desert";
app.locals.location = "Unknown";
// use 方法，添加响应
// use 方法 可以响应任何方式的请求
app.use("/a.html", (req, res) => {
  // send 或 end 返回数据
  // res.write({a: "a", b: "b"}); // 报错 The first argument must be one of type string or Buffer
  res.send({ a: "a", b: "b" });
  res.end("1111");
  // res.send("test");
  // res.end("11111");
});

// 当监听同一个请求时，多个 use 相当于链式操作，按从上往下的顺序依次执行
// 如果不调用 next 方法，链式操作将无法进行执行
app.use("/b.html", (req, res, next) => {
  // 假设，该步，读取数据库
  console.log("1");
  next();
});
app.use("/b.html", (req, res, next) => {
  // 假设，该步，生成页面内容
  console.log("2");
});

// 签名不能够加密，只能防止篡改
// 非必要数据不要增加签名，因为签名后的 cookie 变大，造成了空间浪费
app.get("/a", (req, res) => {
  // req.secret = signedStr; // 签名秘钥, 使用 cookie-parser 会自动声明

  // 发送 cookie
  res.cookie("user", "blue", {
    // domain: '.example.com',
    // path: '/admin',
    // secure: true,
    // expires: new Date(Date.now() + 900000),
    maxAge: 1 * 24 * 3600 * 1000, // 过期时间，单位毫秒
    // httpOnly: true,
    signed: true,
  });

  // 读取 cookie 使用 cookie-parser 中间件
  console.log(req.cookies, req.signedCookies, "cookies");

  // res.end('hello');
  console.log(req);
  res.render("index.html");
  // res.send()
  // res.render("index.html");
  res.send("ok");
});

app.get("/", (req, res) => {
  res.send("ok 1");
});

/**
 * 配置 test 路由
 * @param  {[type]} req  [客户端发过来的请求所带数据]
 * @param  {[type]} res  [服务端的相应对象，可使用res.send返回数据，res.json返回json数据，res.down返回下载文件]
 */
app.get("/test", function (req, res) {
  console.log("object");
  // 使用 mockjs 返回格式化json数据
  res.json(
    Mock.mock({
      status: 200,
      data: {
        "list|1-9": [
          {
            "name|5-8": /[a-zA-Z]/,
            "id|+1": 1,
            "value|0-500": 20,
            img: Mock.Random.image("200x100", "#894FC4", "#FFF", "png"),
          },
        ],
      },
    })
  );
});

app.post("/api/test1", (req, res) => {
  console.log(req, res);
  // const params = req.query;
  res.json({
    code: 200,
    msg: "成功",
  });
});

// app.get([path,] callback) 中的 callback 只能是函数
// 通过 请求类型和uri结合来定义路由
// app.get('/name/:id', (req, res) => {
//   next(new Error('自定义异常~'));
//   // console.log(req);
//   // const { id } = req.params;
//   // res.json({
//   //   id
//   // });
// });

// app.all() 客户端无论使用什么请求方式（get/post/put/delete）都会响应
// path 参数值为 *，客户端无论使用任何 uri ，服务器都会响应，常应用于日志
// app.all('*', (req, res) => {
//   res.end('hello')
// })
// app.all('*', (req, res) => {
//   // res.end('hello')
//   // res.sendFile(__dirname, 'assets/test.html');
// })

// 异常处理
// 异常捕获
//  express 内置异常处理
// 自定义异处理
// function errMiddlewar(err, req, res, next) {
//   if (err) {
//     res.status(500)
//     .json({
//       msg: '服务器异常'
//     })
//   }
// }

// 404
// function noFoundHandler(req, res, next) {
//   res.json({
//     msg: 'api 不存在'
//   })
// }
// app.use(noFoundHandler);
// app.use(errMiddlewar);

// app.use((err, req, res, next) => {
//   if (err) {
//     res.status(500).json({
//       msg: err.messgae,
//     });
//   }
// });

// catch 404 and forward to error handler
// app.use((req, res, next) => {
//   const err = new Error("Not Found");
//   err.status = 404;
//   next(err);
// });

// development error handler
// will print stacktrace
// if (app.get("env") === "development") {
//   app.use((err, req, res, next) => {
//     res.status(err.status || 500);
//     res.render("error", {
//       message: err.message,
//       error: err,
//     });
//   });
// }

// production error handler
// no stacktraces leaked to user
// app.use((err, req, res, next) => {
//   res.status(err.status || 500);
//   res.render("error", {
//     message: err.message,
//     error: {},
//   });
// });

// error handler
// app.use(function(err, req, res, next) {
//   // set locals, only providing error in development
//   res.locals.message = err.message;
//   res.locals.error = req.app.get('env') === 'development' ? err : {};

//   // render the error page
//   res.status(err.status || 500);
//   res.render('error');
// });


// 错误处理  404 自定义
//错误处理
// app.get("*", (req, res) => {
//   res.send("404 not found", 404);
// });

// 为了防止查询数据库时嵌套，可采用 链式 操作 将第一步查询的结果保存在 res 上，之后 next 即可
app.get("/test/user", (req, res, next) => {
  console.log('----[[[');
  // 存在的问题：每当接受到一个请求时，都需要重新连接一次数据库，应该保持一个连接，以提高性能，防止数据库压力过大
  // 只保持一个连接时，多个用户访问时前一个查询未完成，后续用户都需要等着，建议保持二十个连接
  // 连接池
  // const pool = mysql.createPool({
  //   host: "192.168.1.67",
  //   user: "admin",
  //   password: "Zhang@123456",
  //   database: "20210226",
  //   waitForConnections: true,
  //   connectionLimit: 10,
  //   queueLimit: 0,
  // });

  // 查询
  // pool.query("SELECT * FROM `user`", function (err, rows, fields) {
  //   // Connection is automatically released when query resolves
  //   console.log(rows, "rows");
  //   console.log(fields, "fields");
  //   if (err) {
  //     res.status("500").send("database error").end();
  //   } else {
  //     res.render("index.ejs", { banners: data });
  //   }
  // });

  // // 1. 连接数据库
  // const connection = mysql.createConnection({
  //   host: "192.168.1.67",
  //   user: "admin",
  //   password: "Zhang@123456",
  //   database: "20210226",
  // });

  // // 2. 查询
  // connection.query("SELECT * FROM `user`", function (err, results, fields) {
  //   console.log(results, "results"); // results contains rows returned by server
  //   console.log(fields, "fields"); // fields contains extra meta data about results, if available
  // });
});

// 这一行很重要，当服务退出的时候，需要把日志给记录完（记录日志是一个异步的过程）
process.on("exit", () => {
  log4js.shutdown();
});

// 监听服务端口
// const server = app.listen("9000", "0.0.0.0", () => {
//   console.log("server staring ...");
// });
// const server = app.listen("8888", "127.0.0.1", () => {
//   console.log("server staring ...");
// });

httpServer.listen("9090", () => {
  // NODE_ENV 环境变量，默认 undefined
  // git bash 中
  // $ NODE_ENV=production node app.js
  // cmd 中
  // set NODE_ENV=production //设置好环境
  // 启动 node app.js
  // 之后即可通过 process.env.NODE_ENV 来获取
  console.log(process.env.NODE_ENV, "server with socket staring ...");
});

// 数据库初始化
// 1.创建一个数据库
// 2.使用 ‘sequelize cli’ 初始化项目的数据库配置信息 npx sequelize init
// 3.生成模型文件，migrate 文件、model文件 npx sequlize model: generate --name todo --attributes name:string,deadline:date,content:string
// 4.持久化，模型对应的数据库表 npx sequelize db:migrate
// ORM(Objet Relational Mapping) 对象管理模型
// ORM这么一个对象与数据之间的映射技术。简单来说ORM就是通过实例对象的语法，完成对关系型数据库操作的技术，是对象-关系映射的缩写
// 将文档数据库中的一个文档，关系数据库表中的一行，映射为 JavaScript 中的一个对象
// 操作对象，便可以完成对数据库的操作
// Waterline 特点
// 支持大部分的主流数据库
// 脱离 SQL
// 使用同样的代码操作不同的数据库
// Sequelize 作用
//  nodejs 集成 Sequelize
// node application -- ORM(sequelize) --驱动（node-mysql）--- mysql db
// 注意mysql是驱动，我们不直接使用，但是sequelize会用。

